﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SharpAnalyzer.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace SharpAnalyzer.Utilities
{
    // This class will be used to create our output names out of types and their blongings
    internal sealed class MemberNameResolver
    {
        private readonly SemanticModel semanticModel;

        public MemberNameResolver(SemanticModel semanticModel)
        {
            this.semanticModel = semanticModel;
        }

        public bool TryResolveMemberSignatureString(Member member, out string signature)
        {
            signature = (string)null;
            SyntaxNode syntaxNode = member.SyntaxNode;
            switch (member.Kind)
            {
                case MemberKind.Method:
                    signature = GetMethodSignatureString((MethodDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.Constructor:
                    signature = GetConstructorSignatureString((ConstructorDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.Destructor:
                    signature = GetDestructorSignatureString((DestructorDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.GetProperty:
                    signature = GetPropertyGetterSignatureString((PropertyDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.SetProperty:
                    signature = GetPropertySetterSignatureString((PropertyDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.AddEventHandler:
                    signature = GetAddEventHandlerSignatureString((EventDeclarationSyntax)syntaxNode);
                    break;
                case MemberKind.RemoveEventHandler:
                    signature = GetRemoveEventHandlerSignatureString((EventDeclarationSyntax)syntaxNode);
                    break;
                default: // Not found
                    return false;
            }

            return !string.IsNullOrEmpty(signature);
        }

        #region Convert Kind
        private string GetMethodSignatureString(MethodDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            AppendMethodIdentifier(syntax, builder);
            AppendTypeParameters(syntax, builder);
            AppendParameters(syntax, builder);
            AppendReturnType(syntax, builder);
            return builder.ToString();
        }

        private string GetConstructorSignatureString(ConstructorDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            AppendMethodIdentifier(syntax, builder);
            AppendParameters(syntax, builder);
            return builder.ToString();
        }

        private string GetDestructorSignatureString(DestructorDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            AppendMethodIdentifier(syntax, builder);
            return builder.ToString();
        }

        public string GetPropertyGetterSignatureString(PropertyDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            // check
            syntax.AccessorList.Accessors
                .Where(x => x.Kind() == SyntaxKind.GetAccessorDeclaration)
                .SingleOrDefault();
            AppendMethodIdentifier(syntax, builder);
            builder.Append(".get()");
            AppendReturnType(syntax, builder);
            return builder.ToString();
        }

        public string GetPropertySetterSignatureString(PropertyDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            // check
            syntax.AccessorList.Accessors
                .Where(x => x.Kind() == SyntaxKind.SetAccessorDeclaration)
                .SingleOrDefault();
            AppendMethodIdentifier(syntax, builder);
            builder.Append(".set");
            AppendParameters(syntax, builder);
            builder.Append(" : void");
            return builder.ToString();
        }

        private string GetAddEventHandlerSignatureString(EventDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            // check
            syntax.AccessorList.Accessors
                .Where(x => x.Kind() == SyntaxKind.SetAccessorDeclaration)
                .SingleOrDefault();
            AppendMethodIdentifier(syntax, builder);
            builder.Append(".add");
            AppendParameters(syntax, builder);
            builder.Append(" : void");
            return builder.ToString();
        }

        private string GetRemoveEventHandlerSignatureString(EventDeclarationSyntax syntax)
        {
            StringBuilder builder = new StringBuilder();

            // check
            syntax.AccessorList.Accessors
                .Where(x => x.Kind() == SyntaxKind.SetAccessorDeclaration)
                .SingleOrDefault();
            AppendMethodIdentifier(syntax, builder);
            builder.Append(".remove");
            AppendParameters(syntax, builder);
            builder.Append(" : void");
            return builder.ToString();
        }
        #endregion

        private void AppendReturnType(MethodDeclarationSyntax syntax, StringBuilder builder)
        {
            SymbolInfo symbolInfo = semanticModel.GetSymbolInfo((SyntaxNode)syntax.ReturnType);
            ITypeSymbol symbol;
            if ((symbol = symbolInfo.Symbol as ITypeSymbol) == null)
                return;
            string str = ResolveTypeName(symbol);
            builder.AppendFormat(" : {0}", (object)str);
        }

        private void AppendReturnType(PropertyDeclarationSyntax syntax, StringBuilder builder)
        {
            SymbolInfo symbolInfo = semanticModel.GetSymbolInfo((SyntaxNode)syntax.Type);
            ITypeSymbol symbol;
            if ((symbol = symbolInfo.Symbol as ITypeSymbol) == null)
                return;
            string str = ResolveTypeName(symbol);
            builder.AppendFormat(" : {0}", (object)str);
        }

        private void AppendParameters(BaseMethodDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append("(");
            ParameterListSyntax parameterList = syntax.ParameterList;
            if (parameterList != null)
            {
                SeparatedSyntaxList<ParameterSyntax> parameters = parameterList.Parameters;
                if (parameters.Any())
                {
                    string retStr = string.Join(", ", parameters.Select(x =>
                    {
                        StringBuilder stringBuilder = new StringBuilder();
                        string str1 = string.Join(" ", x.Modifiers.Select(m => m.ValueText));
                        if (!string.IsNullOrEmpty(str1))
                        {
                            stringBuilder.Append(str1);
                            stringBuilder.Append(" ");
                        }
                        SymbolInfo symbolInfo = semanticModel.GetSymbolInfo((SyntaxNode)x.Type);
                        ITypeSymbol symbol;
                        if ((symbol = symbolInfo.Symbol as ITypeSymbol) == null)
                            return "?";
                        string str2 = ResolveTypeName(symbol);
                        if (!string.IsNullOrWhiteSpace(str2))
                            stringBuilder.Append(str2);
                        return stringBuilder.ToString();
                    }));
                    builder.Append(retStr);
                }
            }
            builder.Append(")");
        }

        private void AppendParameters(PropertyDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append("(");
            SymbolInfo symbolInfo = semanticModel.GetSymbolInfo((SyntaxNode)syntax.Type);
            ITypeSymbol symbol;
            if ((symbol = symbolInfo.Symbol as ITypeSymbol) != null)
            {
                string str = ResolveTypeName(symbol);
                builder.Append(str);
            }
            builder.Append(")");
        }

        private void AppendParameters(EventDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append("(");
            SymbolInfo symbolInfo = semanticModel.GetSymbolInfo((SyntaxNode)syntax.Type);
            ITypeSymbol symbol;
            if ((symbol = symbolInfo.Symbol as ITypeSymbol) != null)
            {
                string str = ResolveTypeName(symbol);
                builder.Append(str);
            }
            builder.Append(")");
        }

        private void AppendTypeParameters(MethodDeclarationSyntax syntax, StringBuilder builder)
        {
            if (syntax.TypeParameterList == null)
                return;
            SeparatedSyntaxList<TypeParameterSyntax> parameters = syntax.TypeParameterList.Parameters;
            if (!parameters.Any())
                return;
            string str = string.Join(", ", parameters.Select(x => x.Identifier.ValueText));
            builder.AppendFormat("<{0}>", (object)str);
        }

        private void AppendMethodIdentifier(MethodDeclarationSyntax syntax, StringBuilder builder)
        {
            ExplicitInterfaceSpecifierSyntax interfaceSpecifier;
            IdentifierNameSyntax identifierNameSyntax;
            if ((interfaceSpecifier = syntax.ExplicitInterfaceSpecifier) != null &&
                (identifierNameSyntax = interfaceSpecifier.Name as IdentifierNameSyntax) != null)
            {
                string valueText = identifierNameSyntax.Identifier.ValueText;
                builder.AppendFormat("{0}.", (object)valueText);
            }
            builder.Append(syntax.Identifier.ValueText);
        }

        private void AppendMethodIdentifier(ConstructorDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append(syntax.Identifier.ValueText);
        }

        private void AppendMethodIdentifier(DestructorDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append(syntax.Identifier.ValueText);
        }

        private void AppendMethodIdentifier(PropertyDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append(syntax.Identifier.ValueText);
        }

        private void AppendMethodIdentifier(EventDeclarationSyntax syntax, StringBuilder builder)
        {
            builder.Append(syntax.Identifier.ValueText);
        }

        private string ResolveTypeName(ITypeSymbol symbol)
        {
            StringBuilder stringBuilder = new StringBuilder();
            bool flag = false;
            IArrayTypeSymbol arrayTypeSymbol;
            if ((arrayTypeSymbol = symbol as IArrayTypeSymbol) != null)
            {
                flag = true;
                symbol = arrayTypeSymbol.ElementType;
            }
            stringBuilder.Append(symbol.Name);
            INamedTypeSymbol namedTypeSymbol;
            if ((namedTypeSymbol = symbol as INamedTypeSymbol) != null &&
                namedTypeSymbol.TypeArguments.Any())
            {
                IEnumerable<string> values =
                    namedTypeSymbol.TypeArguments.Select(ResolveTypeName);

                stringBuilder.AppendFormat("<{0}>", string.Join(", ", values));
            }
            if (flag)
                stringBuilder.Append("[]");
            return stringBuilder.ToString();
        }
    }
}
